package online.chazi.bootplus.commons.web.resolver;

import online.chazi.bootplus.commons.web.annotation.HeaderBean;
import online.chazi.bootplus.commons.web.annotation.HeaderValue;
import org.apache.commons.beanutils.ConvertUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.core.MethodParameter;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer;

import java.beans.BeanInfo;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;

/**
 * 说明：这是一个非常好用的注解工具。
 * 配合注解 @HeaderBean 和 @HeaderValue 使用。
 * 需要在你的boot工程加入WebMvc配置，如：
 * @ Configuration
 * public class MvcConfig implements WebMvcConfigurer {
 *
 *     @ Override
 *     public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
 *         resolvers.add(new HeaderResolver());
 *     }
 *
 * }
 * 然后在你的Controller方法的参数中使用：
 * @ RestController
 * public class MyController {
 *     @ RequestMapping
 *     public RR test(@HeaderBean(User.class) User user, @HeaderValue("ip") String ip) {
 *         System.out.println("user ====" + JSON.toJSONString(user));
 *         System.out.println("ip ====" + ip);
 *         return RR.reply(user);
 *     }
 * }
 * Created by 叉子同学 on 2020-04-10 23:59
 */
public class HeaderResolver implements HandlerMethodArgumentResolver {

    @Override
    public boolean supportsParameter(MethodParameter methodParameter) {
        return methodParameter.hasParameterAnnotation(HeaderBean.class)
                || methodParameter.hasParameterAnnotation(HeaderValue.class);
    }

    @Override
    public Object resolveArgument(MethodParameter methodParameter, ModelAndViewContainer modelAndViewContainer, NativeWebRequest nativeWebRequest, WebDataBinderFactory webDataBinderFactory) throws Exception {

        HeaderBean headerBean = methodParameter.getParameterAnnotation(HeaderBean.class);
        if (headerBean != null) {
            Class<?> clazz = headerBean.value();
            return genHeaderBean(clazz, nativeWebRequest);
        }

        return genHeaderValue(methodParameter, nativeWebRequest);

    }

    private Object genHeaderValue(MethodParameter methodParameter, NativeWebRequest nativeWebRequest) {
        HeaderValue headerValue = methodParameter.getParameterAnnotation(HeaderValue.class);
        if (headerValue != null) {
            String key = headerValue.value();
            if ("".equals(key)) return null;
            Object value = nativeWebRequest.getHeader(key);
            if (value != null) {
                Class<?> valType = methodParameter.getParameterType();
                return ConvertUtils.convert(value, valType);
            }
        }
        return null;
    }

    private Object genHeaderBean(Class<?> clazz, NativeWebRequest nativeWebRequest) throws Exception {
        if (clazz != Object.class && !clazz.isInterface()) {
            Object object = BeanUtils.instantiateClass(clazz);
            BeanInfo beanInfo = Introspector.getBeanInfo(object.getClass());
            PropertyDescriptor[] propertyDescriptor = beanInfo.getPropertyDescriptors();
            for (PropertyDescriptor property : propertyDescriptor) {
                String key = property.getName();
                if (key.compareToIgnoreCase("class") == 0) {
                    continue;
                }
                String value = nativeWebRequest.getHeader(key);
                if (value != null) {
                    Method setter = property.getWriteMethod();
                    if (setter != null) {
                        Class<?> valType = property.getPropertyType();
                        setter.setAccessible(true);
                        setter.invoke(object, ConvertUtils.convert(value, valType));
                    }
                }
            }
            return object;

        }
        return null;
    }

}
